package xj.graph2d;

import static xj.app.BaseApplicationFrame.getResourceString;
import static xj.app.BaseApplicationFrame.isLinuxOS;
import static xj.app.BaseApplicationFrame.isMacOS;
import static xj.app.BaseApplicationFrame.isWindowsOS;
import static xj.app.BaseApplicationFrame.isWindowsVistaOS;
import static xj.app.BaseApplicationFrame.logInfo;

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

public class FontMap {

  public class FontMapEntry {
    int id;

    FontAttr attr;

    Font font;
  }

  public static FontMap getInstance() {
    if (theInstance == null) {
      theInstance = new FontMap();
    }
    return theInstance;
  }

  public int findFont(String name, int style, int size) {
    FontAttr fa = new FontAttr(name, style, size);
    return findFont(fa);
  }

  public int findFont(FontAttr fa) {
    int n = map.size();
    for (int i = 0; i < n; i++) {
      FontMapEntry entry = map.get(i);
      if (entry != null && fa.equals(entry.attr)) {
	return i;
      }
    }
    FontMapEntry newEntry = new FontMapEntry();
    newEntry.id = n;
    newEntry.attr = fa;

    String fontName = getPlatformFontName(fa.fontName);
    newEntry.font = new Font(fontName, fa.fontStyle, fa.fontSize);

    if (debug) 
      System.out.println("FonMap.findFont() new font: fontAttr=[" + fa + "] font=" + newEntry.font);
    //logInfo("FonMap.findFont(): fontAttr=[" + fa + "] font=" + newEntry.font);

    map.add(newEntry);
    return n;
  }

  public FontAttr getFontAttr(int id) {
    int n = map.size();
    if (id >= 0 && id < n) {
      FontMapEntry entry = map.get(id);
      return entry.attr;
    } else {
      return null;
    }
  }

  public Font getFont(int id) {
    int n = map.size();
    if (id >= 0 && id < n) {
      FontMapEntry entry = map.get(id);
      return entry.font;
    } else {
      return null;
    }
  }

  public FontMetrics getFontMetrics(int id) {
    if (g2 != null) {
      int n = map.size();
      if (id >= 0 && id < n) {
	FontMapEntry entry = map.get(id);
	return g2.getFontMetrics(entry.font);
      }
    }
    return null;
  }

  public Rectangle2D getStringBounds(String text, int font) {
    if (text != null && frc != null) {
      Font f = getFont(font);
      if (f != null) {
	TextLayout tl = new TextLayout(text, f, frc);
	return tl.getBounds();
      }
    }
    return null;
  }

  public int getStringWidth(String text, int font) {
    if (text != null) {
      Rectangle2D bounds = getStringBounds(text, font);
      if (bounds != null) {
	return (int) bounds.getWidth();
      }
    }
    return 0;
  }

  // static void initFontRenderContext(Graphics2D g2) {

  protected static final boolean debug = false;

  protected static FontRenderContext frc = null;

  protected static Graphics2D g2 = null;

  static {
    if (debug) {
      System.out.println("FontMap.initFontRenderContext");
    }
    setFontRendingContext(true);
  }

  public static void setFontRendingContext(boolean fractionalMetrics) {
    GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice gd = ge.getDefaultScreenDevice();
    GraphicsConfiguration gc = gd.getDefaultConfiguration();
    BufferedImage img = gc.createCompatibleImage(1, 1);
    Graphics2D g2 = img.createGraphics();
    if (fractionalMetrics) {
      g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
			  RenderingHints.VALUE_FRACTIONALMETRICS_ON);
    } else {
      g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
			  RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
    }
    if (g2 != null) {
      frc = g2.getFontRenderContext();
      FontMap.g2 = g2;
    }
  }

  public String getPlatformFontName(String name) { 
    String fontName; 
    if (fontNameMap == null) { 
      fontNameMap = new HashMap<String, String>();
      String platformName = null;
      if (isMacOS()) { 
	platformName = "Mac";
      } else if (isLinuxOS()) { 
	platformName = "Linux";
      } else if (isWindowsVistaOS()) { 
	platformName = "WindowsVista";
      } else if (isWindowsOS()) { 
	platformName = "Windows";
      }
      if (platformName != null) { 
	String[] fontNames = { "SansSerif", "Serif", "Monospaced"}; 
	for (int i = 0; i < fontNames.length; i++) { 
	  String names = getResourceString("Font." + platformName + "." + fontNames[i]);
	  if (names != null) { 
	    StringTokenizer st = new StringTokenizer(names, ";");
	    while (st.hasMoreTokens()) { 
	      fontName = st.nextToken();
	      if (fontName != null) { 
		fontName = fontName.trim();
		if (isFontAvailable(fontName)) { 
		  fontNameMap.put(fontNames[i], fontName);
		  break;
		}
	      }
	    }
	  }
	}
      } else { 
	return name;
      }
    }
    fontName = fontNameMap.get(name);
    if (fontName != null) { 
      return fontName; 
    } else { 
      return name; 
    }
  }

  static boolean isFontAvailable(String name) { 
    if (name != null) { 
      String[] fontNameList = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
      for (int i = 0; i < fontNameList.length; i++) { 
	if (name.equals(fontNameList[i])) { 
	  return true;
	}
      }
    }
    return false; 
  }

  protected FontMap() {
  }

  protected List<FontMapEntry> map = new ArrayList<FontMapEntry>();

  protected Map<String, String> fontNameMap = null;

  protected static FontMap theInstance = null;

}
